module(..., package.seeall)
--- 模块功能：完善luat的c库接口
-- @module clib
-- @author openLuat
-- @license MIT
-- @copyright openLuat
-- @release 2017.9.20
local uartReceiveCallbacks = {}
local uartSentCallbacks = {}
local uartstate
local i = 0
abc = false

--- 注册串口事件的处理函数
-- @number id 串口ID: 1表示串口1，2表示串口2，uart.ATC表示虚拟AT口
-- @string event 串口事件:
-- "recieve"表示串口收到数据，注意：使用uart.setup配置串口时，第6个参数设置为nil或者0，收到数据时，才会产生"receive"事件
-- "sent"表示串口数据发送完成，注意：使用uart.setup配置串口时，第7个参数设置为1，调用uart.write接口发送数据之后，才会产生"sent"事件
-- @function[opt=nil] callback 串口事件的处理函数
-- @return nil
-- @usage
-- uart.on(1,"receive",rcvFnc)
-- uart.on(1,"sent",sentFnc)
uart.on = function(id, event, callback)
    if event == "receive" then
        uartReceiveCallbacks[id] = callback
    elseif event == "sent" then
        uartSentCallbacks[id] = callback
    end
end

rtos.on(rtos.MSG_UART_RXDATA, function(id, length)
    if uartReceiveCallbacks[id] then
        uartReceiveCallbacks[id](id, length)
    end
end)

rtos.on(rtos.MSG_UART_TX_DONE, function(id)
    if uartSentCallbacks[id] then
        uartSentCallbacks[id](id)
    end
end)

--- 单次最大串口数据长度，防止撑爆TCP_MSS
local TCP_MSS = 4380
local uarts, mt = {}, {}
mt.__index = mt
--- 过滤空字符串
local function pipe(id)
    local str = ""
    repeat
        str = table.remove(uarts[id].output, 1)
    until str ~= ""
    if str then
        uarts[id].sendwait = "sent"
        log.info("uart " .. id .. " has sent bytes:", uart.write(id, str))
        return true
    else
        uarts[id].sendwait = "idle"
        log.info("uart " .. id .. " has sent done!", "OK")
        return false
    end
end

--- 创建串口对象
-- @number id: 0-1-2 串口硬件id
-- @number baud: 波特率，可选1200，2400，4800，9600，10400，14400，19200，28800，38400，57600，115200，230400，460800，921600
-- @number databits: 数据位7 或 8
-- @number parity: 校验位，可选uart.PAR_EVEN, uart.PAR_ODD或uart.PAR_NONE
-- @number stopbits: 停止位，可选uart.STOP_1，uart.STOP_2
-- @number pio: 485外设控制GPIO编号,例如pio.P2_0 也可以填64
-- @number level: 485外设发送电平,1高电平发送, 0低电平发送
function uart.new(id, baud, databits, parity, stopbits, pio, level, block)
    uart.setup(id, baud, databits or 8, parity or 2, stopbits or 0, 0, 1)
    if tonumber(pio) then
        uart.set_rs485_oe(id, tonumber(pio), tonumber(level))
    end
    uarts[id] = {
        id = id,
        sco = nil, -- send 线程
        rco = nil, -- recv 线程
        input = "",
        output = {},
        wait = "idle",
        block = block,
        callbacks = {},
        sendwait = "idle",
        flen = {["*l"] = 1, ["*L"] = 2, ["*s"] = 1, ["*n"] = 1, ["*p"] = 1},
        ft = {["*l"] = "\n", ["*L"] = "\r\n", ["*s"] = "%s", ["*n"] = "%d", ["*p"] = "%p"}
    }
    uartSentCallbacks[id] = function(id)
        if not pipe(id) then
            if uarts[id].callbacks.sent then uarts[id].callbacks.sent() end
            if uarts[id].sco then coroutine.resume(uarts[id].sco, true) end
        end
    end
    uartReceiveCallbacks[id] = function(id, length)
        local t = {uarts[id].input}
        repeat
            t[#t + 1] = uart.read(id, length or 8192)
        until t[#t] == ""
        uarts[id].input = table.concat(t)
        -- log.info("uart.read length:", uarts[id].wait, #uarts[id].input)
        -- if uarts[id].wait == "recv" then coroutine.resume(uarts[id].rco, true) end
        coroutine.resume(uarts[id].rco, true)
    end
    return setmetatable(uarts[id], mt)
end
--- 注册截止符
-- @string word : recv(format,timeout)的format 可选参数
-- @string val : format 对应的正则表达式
-- @string len : 正则表达式匹配关键字的最大长度
-- @return nil
-- @usage mt:regFormat("*S"," ",1)
-- @usage mt:regFormat("*xyz","xyz",3)
-- @usage mt:regFormat("*t","[%s%p]",1)
function mt:regFormat(cutWord, val, len)
    if not cutWord or not val then return end
    len = tonumber(len) and tonumber(len) or 1
    self.ft[cutWord], self.flen[cutWord] = val, len
end
--- 读取缓冲区数据长度
-- @return number 缓冲区数据长度
-- @usage local len = mt:getLenth()
function mt:getLength()
    return #self.input
end
--- 清空缓冲区数据长度
-- @return nil
-- @usage mt:clear()
function mt:clear()
    self.input = ""
end
--- 读取串口数据
-- @string[opt=nil,number] format:
--      nil:读取到任意字符返回
--      *l：读取到结束字符\n或超时
--      *s：读取到空格字符 或超时
--      数字，number类型：读取或者阻塞读取number长度的数据
-- @number[opt=nil] timeout: ms,缓冲区非空读取超时,nil表示一直等待
-- @boolean[opt=nil] option 默认nil或false,读取超时返回"",为其它值超时读取所有数据
-- @number[opt=nil] syncwait 默认nil,无数据等待超时
-- @return string 读取到的数据
-- @return boolean 读取成功返回true,超时返回false
-- @usage mt:recv(nil,25)
function mt:recv(format, timeout, option, syncwait)
    local line, idx = nil, 1
    -- self.rco, self.wait = coroutine.running(), "recv"
    self.rco = coroutine.running()
    repeat
        if #self.input > TCP_MSS then
            line = self.input
        elseif type(format) == "number" then
            line = #self.input >= format and self.input:sub(1, format)
        elseif self.ft[format] then
            local _, endidx = self.input:find(".-" .. self.ft[format], idx)
            if endidx then
                -- log.info("正常的")
                line = self.input:sub(1, endidx)
            else
                -- log.info("一二三")
                idx = #self.input > self.flen[format] and #self.input - self.flen[format] or 1
            end
        elseif type(timeout) == "number" then
            option = true
        else
            line = #self.input > 0 and self.input
        end
        -- log.info("循环",line)
        if line then
            if line:find("GOKE") or line:find("URANUS5") or line:find("UC6226") then
                i = 0
            end
        end
        -- log.info("循环",line,sys.wait(#self.input > 0 and timeout or syncwait),(#self.input > 0 and timeout or syncwait),self.input:toHex())
        if uartstate  and line == nil then
            i= i + 1
            -- log.info("开始执行",i)
            if i==5 then
                abc = true
                break
            end
        end
    until line or not sys.wait(#self.input > 0 and timeout or syncwait)
    -- self.wait = "idle"
    if option and not line then line = self.input end
    if line then self.input = self.input:sub(#line + 1, -1) end
    if self.callbacks.recv then self.callbacks.recv(line, line and true or false) end
    return line or "", (line and line ~= "") and true or false
end
--- 串口发送函数
-- @string[opt=char,string] ... : 要发送的数据
--      参数1: 8位整数 或 小于3K的字符串
--      参数2: 8位整数 或 小于3K的字符串
--      参数n：8位整数 或 小于3K的字符串
-- @retur nil
function mt:write(...)
    local cnt = 0
    self.output = table.merge(self.output, {...})
    if self.sendwait == "idle" then
        if self.block then
            while #self.output > 0 do
                self.sendwait = "sent"
                local len = uart.write(self.id, self.output[1])
                if len ~= 0 then
                    cnt = cnt + len
                    table.remove(self.output, 1)
                end
            end
            log.info("uart_blcok " .. self.id .. " has sent bytes:", cnt)
        else
            return pipe(self.id)
        end
    end
    return cnt
end
--- 串口发送函数
-- @string data: 要发送的数据(支持大于3K)
-- @retur nil
function mt:send(data, timeout)
    self.sco = coroutine.running()
    if self.sco and self:write(unpack(data:split(6000))) then
        return sys.wait(timeout)
    end
end
--- 销毁串口对象,关闭串口
-- @return nil
-- @usage mt:destroy()
function mt:destroy()
    uart.close(self.id)
    uartSentCallbacks[self.id] = nil
    uartReceiveCallbacks[self.id] = nil
    uarts[self.id], self = nil, nil
end
--- mt:on 注册函数
-- @string event,事件，可选值"recv","sent"
-- @function callback,回调函数,会给"recv"传递两个参数
--      data  :该串口读取到的数据
--      result:读取的条件是否满足
-- @usage mt:on("recv",function(data,result)print("收到的数据",data,result)end)
-- @usage mt:on("sent",function() print("发送已完成!")end)
function mt:on(event, callback)
    self.callbacks[event] = callback
end
--- 启动回调守护函数，需要在任务中使用
-- @string[opt=number,nil] format:
--      nil:读取到任意字符返回
--      *l：读取到结束字符\n或超时
--      *s：读取到空格字符 或超时
--      数字，number类型：读取或者阻塞读取number长度的数据
-- @number[opt=nil] timeout: ms,缓冲区非空读取超时,nil表示一直等待
-- @boolean[opt=nil] option: 默认nil或false,读取超时返回"",为其它值超时读取所有数据
-- @function[opt=nil] proc : 处理的回调函数
-- @number[opt=nil] syncwait: 默认nil,无数据等待超时
-- @return nil
-- @usage sys.taskInit(u1.start, u1, nil, 10)
-- @usage sys.taskInit(u1.start, u1, nil, 10, nil, function(msg)ws:send(msg) end)
function mt:start(format, timeout, option, proc, syncwait)
    while true do
        local msg, res = self:recv(format, timeout, option, syncwait)
        if res and proc then proc(msg) end
    end
end

sys.subscribe("UART_STATE", function ()
    uartstate = true
end)

sys.subscribe("SELECT_END", function ()
    uartstate = false
end)
